/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-08-15     Ernest Chen  the first version
 * 2023-08-15     WangShun     adapt to YOC
 */

#include <string.h>
#include <aos/types.h>

#define PKG_USING_AHT10
#include "aht10x.h"

extern int32_t aos_debug_printf(const char *fmt, ...);
#ifdef PKG_USING_AHT10

static aos_mutex_t mutex = NULL;
#define AHT10_ADDR 0x38 //connect GND

#define AHT10_CALIBRATION_CMD 0xE1 //calibration cmd for measuring
#define AHT10_NORMAL_CMD 0xA8      //normal cmd
#define AHT10_GET_DATA 0xAC        //get data cmd

static signed long write_reg(aht10_device_t dev, unsigned char reg, unsigned char *data)
{
    unsigned char buf[3];

    buf[0] = reg; //cmd
    buf[1] = data[0];
    buf[2] = data[1];

    if (rvm_hal_iic_master_send(dev->i2c, AHT10_ADDR, (uint8_t *)buf, 3, 100u) == 0)
        return 0;
    else
        return -1;
}

static signed long read_regs(aht10_device_t dev, unsigned char len, unsigned char *buf)
{
    if (rvm_hal_iic_master_recv(dev->i2c, AHT10_ADDR, (uint8_t *)buf, len, 100u) == 0) 
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

static signed long sensor_init(aht10_device_t dev)
{
    unsigned char temp[2] = {0, 0};

    write_reg(dev, AHT10_NORMAL_CMD, temp);
    aos_msleep(300); //at least 300 ms

    temp[0] = 0x08;
    temp[1] = 0x00;
    write_reg(dev, AHT10_CALIBRATION_CMD, temp); //go into calibration
    aos_msleep(300);   //at least 300 ms

    return 0;
}

/*check calibration enable */
static unsigned char calibration_enabled(aht10_device_t dev)
{
    unsigned char val = 0;

    read_regs(dev, 1, &val);

    if ((val & 0x68) == 0x08)
        return 0;
    else
        return 1;
}

static float read_hw_temperature(aht10_device_t dev)
{
    unsigned char temp[6];
    float cur_temp = -50.0;  //The data is error with missing measurement.
    signed long result;

    CHECK_RET_WITH_RET(dev, -1);

    result = aos_mutex_lock(dev->lock, AOS_WAIT_FOREVER);

    if (result == 0)
    {
        unsigned char cmd[2] = {0, 0};
        write_reg(dev, AHT10_GET_DATA, cmd); // sample data cmd

        result = calibration_enabled(dev);
        if (result != 0)
        {
            aos_msleep(1500);
            sensor_init(dev); // reset sensor
            aos_debug_printf("The aht10 is under an abnormal status. Please try again");
        }
        else
        {
            read_regs(dev, 6, temp); // get data
            /*sensor temperature converse to reality */
            cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
        }
    }
    else
    {
        aos_debug_printf("The aht10 could not respond temperature measurement at this time. Please try again");
    }
    aos_mutex_unlock(dev->lock);

    return cur_temp;
}

static float read_hw_humidity(aht10_device_t dev)
{
    unsigned char temp[6];
    float cur_humi = 0.0;  //The data is error with missing measurement.
    signed long result;

    CHECK_RET_WITH_RET(dev, -1);

    result = aos_mutex_lock(dev->lock, AOS_WAIT_FOREVER);
    if (result == 0)
    {
        unsigned char cmd[2] = {0, 0};
        write_reg(dev, AHT10_GET_DATA, cmd); // sample data cmd

        result = calibration_enabled(dev);
        if (result != 0)
        {
            aos_msleep(1500);
            sensor_init(dev);
            aos_debug_printf("The aht10 is under an abnormal status. Please try again");
        }
        else
        {
            read_regs(dev, 6, temp); // get data
            cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20); //sensor humidity converse to reality
        }
    }
    else
    {
        aos_debug_printf("The aht10 could not respond temperature measurement at this time. Please try again");
    }
    aos_mutex_unlock(dev->lock);

    return cur_humi;
}

/**
 * This function reads temperature by aht10 sensor measurement
 *
 * @param dev the pointer of device driver structure
 *
 * @return the relative temperature converted to float data.
 */
float aht10_read_temperature(aht10_device_t dev)
{
    return read_hw_temperature(dev);
}

/**
 * This function reads relative humidity by aht10 sensor measurement
 *
 * @param dev the pointer of device driver structure
 *
 * @return the relative humidity converted to float data.
 */
float aht10_read_humidity(aht10_device_t dev)
{
    return read_hw_humidity(dev);
}

/**
 * This function initializes aht10 registered device driver
 *
 * @param dev the name of aht10 device
 *
 * @return the aht10 device.
 */

aht10_device_t aht10_init(const char *i2c_bus_name, unsigned int iic_idx)
{
    int result;
    aht10_device_t dev;
    char filename[8] = {0};
    rvm_hal_iic_config_t config = {0};

    dev = calloc(1, sizeof(struct aht10_device));
    if (dev == NULL)
    {
        aos_debug_printf("Can't allocate memory for aht10 device on '%s' ", i2c_bus_name);
        return NULL;
    }

    sprintf(filename, "iic%d", iic_idx);

    /* Register Device */
    rvm_iic_drv_register(iic_idx);

    /* Get device handle */
    dev->i2c = rvm_hal_iic_open(filename);

    /* Initialize as default parameter */
    rvm_hal_iic_config_default(&config);

    /* Configure device parameters */
    rvm_hal_iic_config(dev->i2c, &config);

    if (dev->i2c == NULL)
    {
        aos_debug_printf("Can't find aht10 device on '%s' ", i2c_bus_name);
        free(dev);
        return NULL;
    }

    result = aos_mutex_new(&mutex);
    dev->lock = &mutex;
    if (dev->lock == NULL)
    {
        aos_debug_printf("Can't create mutex for aht10 device on '%s' ", i2c_bus_name);
        free(dev);
        return NULL;
    }

    sensor_init(dev);

    return dev;
}

/**
 * This function releases memory and deletes mutex lock
 *
 * @param dev the pointer of device driver structure
 */
int aht10_deinit(aht10_device_t dev)
{
    CHECK_RET_WITH_RET(dev, -1);

    aos_mutex_free(dev->lock);
    free(dev);
    return 0;
}

#endif /* PKG_USING_AHT10 */
